import React, {
    ReactElement,
    PureComponent,
    cloneElement,
} from 'react';

import {
    View,
    NativeScrollEvent
} from 'react-native';

import PropTypes from 'prop-types';

interface Props {
    lazy: boolean,
    getRef: Function,
    onLayout:Function,
    onScroll:Function,
    children: ReactElement
}

interface LazyScrolls {
    [key:string]:Function
}

import getRect from './rect';

class Scroll extends PureComponent<Props> {
    Ref: any = null;
    ViewRef: any = null;
    height: number = 0;
    scrollY: number = 0;
    offsetTop: number = 0;
    lazyScrolls: LazyScrolls = {};

    static defaultProps = {
        lazy: true,
        getRef: () => null,
        onLayout: () => null,
        onScroll:() => null
    }

    static childContextTypes = {
        scroll: PropTypes.shape({
            ref: PropTypes.func,
            lazy: PropTypes.bool,
            scrollY:PropTypes.func,
            offsetY: PropTypes.func,
            onLazyScroll: PropTypes.func,
            removeLazyScroll:PropTypes.func
        })
    }

    getChildContext() {
        return this.childContext;
    }

    get childContext() {
        const {
            lazy
        } = this.props;
        const _Props = {
            lazy: lazy,
            ref: () => this.Ref,
            offsetY: this._getOffsetY,
            scrollY:() => this.scrollY,
            onLazyScroll: this._onLazyScroll,
            removeLazyScroll:this._removeLazyScroll
        }
        return {
            scroll: _Props
        }
    }

    _getOffsetY = () => {
        return this.height + this.offsetTop;
    }

    _onLazyScroll = (key:string,fn: Function) => {
        this.lazyScrolls[key] = fn;
    }

    _removeLazyScroll =  (key: string) => {
        if(key in this.lazyScrolls) {
            delete this.lazyScrolls[key];
        }
    }

    _onScroll = ({nativeEvent}:any) => {
        const {
            onScroll
        } = this.props;

        const {
            contentOffset
        } = nativeEvent as NativeScrollEvent;

        const {
            y = 0
        } = contentOffset;

        this.scrollY = y;

        this._dispatch();

        onScroll(nativeEvent);
    }

    _dispatch = () => {
        for(let key in this.lazyScrolls) {
            this.lazyScrolls[key]();
        }
    }

    _getRef = (e: any) => {
        const {
            getRef
        } = this.props;
        getRef(e);
        this.Ref = e;
    }

    _onLayout = async ({nativeEvent}:any) => {
        const {
            onLayout
        } = this.props;

        const {
            layout
        } = nativeEvent;

        const {
            height
        } = layout;

        onLayout(layout);

        const {
            top
        } = await getRect(this.ViewRef);

        this.offsetTop = top;
        this.height = height;
    }

    get Props() {
        return {
            ref: this._getRef,
            scrollEventThrottle:16,
            onScroll: this._onScroll
        }
    }

    render() {
        const {
            children
        } = this.props;

        return (
            <View
                style={{flex: 1}}
                onLayout={this._onLayout}
                ref={e => this.ViewRef = e}
            >
                {cloneElement(children, this.Props)}
            </View>
        )
    }
}

export default Scroll;